﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com) & Quincy.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OF.Notify.DataHost.Cluster.Disk
{
    public class StringEqualityComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            if (x == y)
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }
            return x.Equals(y);
        }

        public int GetHashCode(string obj)
        {
            if (obj == null)
            {
                return 0;
            }
            return obj.GetHashCode();
        }
    }

    public class StringLock
    {
        internal class ReferenceCount
        {
            public long Count;
            public string Key;
        }

        internal Dictionary<string, ReferenceCount> dict = new Dictionary<string, ReferenceCount>(new StringEqualityComparer());

        public void Invoke(string key, Action action)
        {
            ReferenceCount referenceCount = this.Begin(key);
            try
            {
                action();
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                this.End(referenceCount);
            }
        }

        internal ReferenceCount Begin(string key)
        {
            ReferenceCount item = null;
            lock (this)
            {
                if (dict.ContainsKey(key))
                {
                    item = dict[key];
                }
                else
                {
                    item = new ReferenceCount { 
                         Count = 0,
                         Key = key
                    };
                    dict.Add(key, item);
                }
                item.Count++;
            }
            System.Threading.Monitor.Enter(item);
            return item;
        }

        internal void End(ReferenceCount item)
        {
            System.Threading.Monitor.Exit(item);
            lock (this)
            {
                item.Count--;
                if (item.Count == 0)
                {
                    dict.Remove(item.Key);
                }
            }
        }
    }
}
